/*************************************************************************
	> File Name: uart.c
	> Author: yuarmy
	> Mail: yuarmy@126.com 
	> Created Time: Sun 03 Aug 2014 05:26:51 AM PDT
 ************************************************************************/

#include <errno.h>
#include <memory.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include "shsocket.h"
#define WRITEFLAGE 0
#define READFLAGE 1
static void WaitFdReady(int fd, int32_t is_read) {
  fd_set fds;
  while(1) {
    FD_ZERO(&fds);
    FD_SET(fd, &fds);
    if (READFLAGE == is_read) {
      select(fd + 1, &fds, NULL, NULL, NULL);
    } else {
      select(fd + 1, NULL, &fds, NULL, NULL);
    }

    if (FD_ISSET(fd, &fds)) {
      return;
    }
  }
}

static int32_t FdWrite(int fd, char* buf, int size) {
  int32_t ret = size;
  int n = size;
  int nwritten = 0;
  char* bufp = buf;
  while (n > 0) {
    WaitFdReady(fd, WRITEFLAGE);
    if ((nwritten = write(fd, bufp, n)) < 0) {
      if (errno == EAGAIN) {
        continue;
      } else {
        ret = -1;
        break;
      }
    } else {
      bufp += nwritten;
      n -= nwritten;
    }
  }
  return ret;
}

int32_t InitSocket(int32_t port, int32_t queue, SHDEVMANAGE_T *shdev) {
  int32_t ret;
  struct sockaddr_in server_address;
  int32_t server_len;
  memset(shdev, 0, sizeof(SHDEVMANAGE_T));
  shdev->server_sockfd = socket(AF_INET, SOCK_STREAM, 0);
  if (shdev->server_sockfd < 0) {
    fprintf(stderr, "%s %d it is error: %d, so fail to open socket!\n",
        __FILE__, __LINE__, errno);
    goto fail;
  }

  server_address.sin_family = AF_INET;
  server_address.sin_addr.s_addr = htonl(INADDR_ANY);
  server_address.sin_port = htons(port);
  server_len = sizeof(server_address);

  ret = bind(shdev->server_sockfd, (struct sockaddr *)&server_address, server_len);
  if (ret < 0) {
    fprintf(stderr, "%s %d it is error: %d, so fail to bind socket!\n",
        __FILE__, __LINE__, errno);
    goto fail;
  }
  listen(shdev->server_sockfd, queue);
  if (ret < 0) {
    fprintf(stderr, "%s %d it is error: %d, so fail to listen socket!\n",
        __FILE__, __LINE__, errno);
    goto fail;
  }

  return ret;
fail:
  if (shdev->server_sockfd > 0) {
    close(shdev->server_sockfd);
  }
  return ret;
}

/*  If the activity is on server_sockfd, it must be a request for a new connection
            and we add the associated client_sockfd to the descriptor set.  */
int32_t OpenNewSocket(SHDEVMANAGE_T *shdev) {
  int32_t ret;
  struct sockaddr_in client_address;
  int32_t client_len;
  int32_t fd;

  client_len = sizeof(client_address);
  fd = accept(shdev->server_sockfd, 
	  (struct sockaddr *)&client_address, &client_len);
  if (fd < 0) {
	fprintf(stderr, "%s %d error: %d, so fail to accept socket!\n",
		__FILE__, __LINE__, errno);
	ret = -1;
	goto fail;
  }

  if (shdev->client_sockfd == 0) {
	shdev->client_sockfd = fd;
	printf("adding client on fd %d\n", shdev->client_sockfd);
  } else if (shdev->test_sockfd == 0) {
	shdev->test_sockfd = fd;
	printf("adding test client on fd %d\n", shdev->test_sockfd);
  }
  ret = fd;
fail:
  return ret;
}

int32_t WriteToSocket(SHDEVMANAGE_T *shdev, uint8_t* buff, int32_t size) {
  int32_t ret;

  ret = FdWrite(shdev->client_sockfd, buff, size);
  return ret;
}
